skills(i18n): validation messages are a translation group since #14253 — correct the claim and its neighbours (net +7 tokens) - #14664
Merged
Conversation
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the write path by the ObjectQL rule evaluator through the existing i18n service, so the blockquote teaching that validation messages are not a translation group is false on main. Correct it and the two neighbouring inventories it sits in: `_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the top-level group list, plus the validation-rule target `os validate` / `os lint` already report but the list omitted. A correction, not an expansion: the retired-key blockquote's substance moves into the `_validations` row it belongs in, which pays the three additions in the same file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the headroom left un-re-locked by #14574 is not spent. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…8n-validations-group
This was referenced Sep 2, 2026
os-zhuang
approved these changes
Sep 2, 2026
os-zhuang
marked this pull request as ready for review
September 2, 2026 22:49
os-zhuang
enabled auto-merge
September 2, 2026 22:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14377
skills/objectstack-i18n/SKILL.mdtaught that validation messages are not a translation group. That was true when written and is false onmaintoday: #14253 (landed as b2eab95, "i18n: give bulk-action defs, custom validation messages and dataset labels a bundle key") added three key families at once, and the published catalog ships to codebases this repo cannot see. This corrects the claim and the two neighbouring inventories it sits in.A correction, not an expansion — maintainer ruling 2026-08-21, quoted verbatim and untranslated: 「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」 Three new key families did not license three new sections: the diff is one added table row, one clause inside an existing row, one word in each of two existing prose lists, and one deleted blockquote. No new section. No ceiling change. The ratchet script is untouched, and the headroom #14574 left un-re-locked is not spent.
Per-claim: where, before, after
Line numbers are on
origin/mainat merge base20b88391.1. The claim itself —
SKILL.md:172(blockquote, 3 lines)Before
After — deleted; its substance moves into the new
_validationsrow below, which is where a reader looking up an object sub-key will actually find it.Why it is false.
objects.OBJECT._validations.RULE.messageis a declared group, object-scoped, with a reader:packages/spec/src/system/translation.zod.ts:427—_validations: z.record(...)onObjectTranslationDataSchema, sibling to_views/_actions/_tabs.messageis the only declared key;label,description,conditionandwheneach carryguidancerejecting them, so the surface is one key by measurement, not by omission.packages/spec/src/system/i18n-resolver.ts:1862—objectValidationMessageKey(objectName, ruleName)returnsobjects.${objectName}._validations.${ruleName}.message.packages/objectql/src/validation/rule-validator.ts:2107— the ObjectQL rule evaluator calls it on the write path and resolves through the existingValidationMessageContext.translatehook (thei18nServicechannel that has localized built-in messages since Field validation messages are hardcoded English + API field name —penalty_amount must be ≥ 0reaches end users verbatim #3957). Authored message on a miss, translation on a hit, no interpolation.The schema's own note (
translation.zod.ts:404-410) states the distinction the old blockquote lost: this is notvalidationMessagesreturning. That group was keyed by rule name at the top level, so it could not tell two objects' rules apart, and nothing read it — which is why 17.0.0 (#4667, ADR-0049) retired it. The new one is object-scoped and has a reader.2. Object sub-key table —
SKILL.md:163(_viewsrow)label,description,emptyState.title/emptyState.messagebulkActions.{def_name}(label,confirmText,confirmLabel,params)packages/spec/src/system/translation.zod.ts:281—bulkActionsis a record under_views.VIEW, keyed byBulkActionDefSchema.name. The four keys named are the ones the def actually declares;successMessageanddescriptioncarryguidancerejecting them, and per-param help is spelledhelp, nothelpText. The row names the declared keys so an author cannot reach for the two that parse-fail.3. Top-level group inventory —
SKILL.md:168-170apps,messages,globalActions,dashboards,pages,flows,settings,metadataForms,settingsCommondatasetsinserted afterdashboardspackages/spec/src/system/translation.zod.ts:770—datasetsis a top-level group onTranslationDataSchema, resolved bytranslateDataset. The inventory was incomplete by exactly one entry; it is corrected in place as a list item, not promoted to a section of its own.4. Unknown-target inventory —
SKILL.md:201packages/lint/src/validate-translation-references.ts:629-634registers_validations.RULEtargets fromobjects[].validations[].name(the code names #14253 at the site). The lint side already reports an unknown validation-rule target; the enumeration simply had not been extended. One word.premise_false— claims checked and left alone because they are true onmainSKILL.md:387-391, the--objects-onlydivergence — true as written.--objects-onlyis on by default and--metadata-formsis orthogonal to it;packages/cli/test/i18n-extract-emitted-files.test.tspins all three directions, including the regression where metadata-forms emission was briefly derived from--objects-only. Untouched — this is what skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) #14574 taught and it stays consistent.SKILL.md:380-386, the generated-bundle file shape — true as written.packages/platform-objects/src/apps/translations/holdsLOCALE.objects.generated.ts,LOCALE.metadata-forms.generated.tsandLOCALE.source-hashes.generated.tsexactly as described. Untouched.SKILL.md:407-436, thewithSourceFallbackwrapping and thekernel:readyload — true as written, and re-verified againstpackages/platform-objects/src/apps/translations/index.ts. Untouched.SKILL.md:462-465, the retiredo.*sibling-key redirect list — correct to omitvalidationMessages.translation.zod.ts:470-480definesLEGACY_OBJECT_FIRST_KEYS, and the schema comment at:532states explicitly thatvalidationMessagesis "not a legacy object-first key". Adding it there would have been a factually wrong placement. Untouched.SKILL.md:281-285, theos i18n checksurface list — the groups it enumerates are the groups the walker actually visits (packages/cli/src/utils/i18n-coverage.ts:185-218,COVERAGE_SOURCE). Untouched. See the note below.Known gap, already filed — not folded into this PR
os i18n extractandos i18n checkwalk none of the three families #14253 added:packages/cli/src/utils/i18n-extract.tsemits no_validations,bulkActionsordatasetspath (its emitted roots are enumerated at:579,:649,:786-862,:928-1001,:1106-1173), andCOVERAGE_SOURCEhas no bucket for any of them. That is #14376 (open,domain:cli,pm:dispatched), already tracking exactly this. A caveat sentence about it was drafted into this file and then removed: it is the CLI's gap, #14376 is the place it is being fixed, and paying for it out of this file's budget would have made the net delta positive for a fact that belongs to another card.Token budget — every added token paid by a deletion in the same file
node scripts/check-skills-token-ratchet.mjs, its own verdict line, before and after:skills/objectstack-i18n/SKILL.mdLines: 496 to 493 (-3). Diff: 7 insertions, 10 deletions. The blockquote deletion is the payment — its substance is carried by the
_validationsrow, so nothing true was dropped to make the budget.scripts/check-skills-token-ratchet.mjsis not in the diff.Gates
Head
d2fa7a18fdb378722342f1cdba9e266f5bb797cd. Family re-derived after the last edit withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(change set: 1 path,skills/objectstack-i18n/SKILL.md; merge base20b883918), run underscripts/pm/os-verify-lock.shwithOS_VERIFY_LOCK_SLOT=issue-14377. Every exit code captured before any pipe.check-skills-token-ratchet✓ 31 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.check:skill-identifier-livenessOK — Leg 1: 465 citation(s) over 41 published file(s) checked against 93441 implementation word tokens (3 ledgered exemption(s)); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).check:skill-docs✅ Skill docs in synccheck:skill-compatibility✓ 11 SKILL.md file(s) reconciled against 79 workspace packagescheck:skill-frame-sync✓ 4 copies of the decision frame are structurally isomorphic across 3 filescheck:role-wordOK(self-test + scan; baseline unchanged, no--update)check:corpus-claim-driftOKcheck:doc-authoring✓ 14500 customer-facing string(s) across 710 spec sources cleancheck:nul-bytesOK (scanned 7995 text file(s); no raw ASCII control bytes)check:agent-test-spelling✓ 0 violations — 425 file(s)check:pm-governed-merges✓ self-test: 243 assertionscheck-ci-filter-parityOK: all 130 declared cross-package glob(s) (92 unique) are coveredcheck-cross-package-test-inputs/check:cross-package-test-inputsOK: 25 package(s) read outside themselves, all declaredcheck-shard-attestation✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).check:skill-docswas green without regenerating anything — the frontmatter did not change, soskills/README.mdandcontent/docs/ai/skills-reference.mdxare not in this diff.NOT MEASURED (prerequisite refusals, never content findings)
check-test-completenessPREREQUISITE NOT MET— it grades a savedturbo run testlog and no log was named. Its own text says the family derives it with no argument and "the local reading for this gate is NOT MEASURED". CI tees the log and passes the path.check:doc-formula-expressionsPREREQUISITE NOT MET — the workspace package @objectstack/lint is not built.(After building@objectstack/formulathe refusal advanced to the next unbuilt package in the chain.)check:skill-examplesPREREQUISITE NOT METin substance —packages/client-react/dist holds no .d.ts declarations — the package is not built. Exit 1 here is the refusal branch, not a finding.Declared narrowing, with the measurement behind it. The shared verify lock was continuously held by four sibling agents (two 9-minute queue timeouts at exit 99 and one foreground cap kill at 10 minutes, with three waiters ahead each time), so the remaining dependency builds those three gates need were not run locally. Rather than assert the narrowing, it is measured: for the two gates whose input is this file,
check:skill-examplestype-checksos:check-marked TypeScript blocks.os:checkmarker count in this file is 2 before and 2 after; fenced-block delimiter count is 24 before and 24 after; andgit diff -U0contains no added or removed line that is a fence or anos:checkmarker. The gate's own census reports 224 blocks on theskills + docssurface — this file's contribution to it is byte-identical toorigin/main.check:doc-formula-expressionsflags CEL field-rule expressions with rejected root identifiers across.claude,docs,skills,content. Every added line is reproduced in section 2-4 above; none contains expression syntax (norecord., nocurrent_user, no operator, no formula).So neither gate's verdict can differ from
main's on account of this diff. Both run on this PR in CI regardless, which is where the authoritative reading is.Landing
skills/**is a governed surface (Prime Directive #14). This PR stays draft: no merge, no queue, no auto-merge, no ready flip. It is a PR visibly awaiting the maintainer's own merge. Labels:skip-changeset(verified againstscripts/check-empty-changeset.mjs:357, which enumeratesskills/among the paths that release nothing) andneeds:contract-review(the corrected rows state which keys the i18n resolver honours — a platform contract claim).🤖 Generated with Claude Code
https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Generated by Claude Code